/* eslint-disable no-underscore-dangle, no-lonely-if */
import { fetch as dvaFetch } from 'dva';
import isEmpty from 'lodash/isEmpty';
import { ApiReturn } from '@/types/global';
import qs from 'qs';
import moment from 'moment';
import { message as Message } from 'antd';

export interface BaseResponseData {
  code: number;
  message: string;
}

interface Options extends RequestInit {
  requestType?: 'json' | 'form';
  responseType?: 'json' | 'blob';
  data?: { [key: string]: any } | FormData; // 支持传入对象或者formData
}

function parseOptions({
  url,
  options = {},
}: {
  url: string;
  options: Options;
}) {
  const headers = {
    ...(options.headers || {}),
  };
  // @ts-ignore
  if (
    !(options.data instanceof FormData) &&
    ['POST', 'PUT', 'DELETE'].includes(options.method)
  ) {
    // @ts-ignore
    headers['Content-Type'] = 'application/json';
  }

  const token = window.localStorage.getItem('token');
  if (token) {
    // @ts-ignore
    headers['token'] = token;
  }

  // eslint-disable-next-line
  let {
    requestType,
    responseType = 'json',
    data = {},
    ...resultOptions
  } = options;

  requestType = requestType || data instanceof FormData ? 'form' : 'json';

  resultOptions = {
    method: 'GET',
    ...resultOptions,
    headers,
    credentials: 'include',
    mode: 'cors',
  };

  let resultUrl = url;
  // get请求把data添加到url参数
  if (resultOptions.method === 'GET') {
    if (!isEmpty(data)) {
      resultUrl += resultUrl.indexOf('?') >= 0 ? '&' : '?';
      resultUrl += qs.stringify(data);
    }
  } else {
    // 走json
    if (requestType === 'json') {
      resultOptions.body = JSON.stringify(data);
    } else {
      // 表单请求或者文件上传， data可传入FormData
      if (data instanceof FormData) {
        resultOptions.body = data;
      } else {
        // 表单请求， data可转换为FormData
        const formData = new FormData();
        const keys = Object.keys(data);
        keys.forEach((key) => {
          formData.append(key, data[key]);
        });
        resultOptions.body = formData;
      }
    }
  }

  if (options.responseType === 'blob') {
    // @ts-ignore
    resultOptions.responseType = 'blob';
  }
  return { url: resultUrl, options: resultOptions };
}

async function parseResponse(response: Response, options: Options) {
  let error;
  if (response.status >= 200 && response.status < 300) {
    //如果responseType 为blob 则是下载文件
    if (options.responseType === 'blob') {
      // @ts-ignore
      const contentDisposition = decodeURIComponent(
        response.headers.get('content-disposition'),
      );
      if (!contentDisposition.match(/filename=(.*?)(;|\s|$)/)) {
        const res = await response.json();
        throw new Error(typeof res.message === 'string' ? res.message : '');
      }
      // @ts-ignore
      const fileName = contentDisposition.match(/filename=(.*?)(;|\s|$)/)[1];
      const res = await response.blob();
      return { blob: res, fileName };
    }
    const res = await response.json();
    res.code = res.code ?? res.status;
    if (res.code === 0) {
      return res;
    } else if (res.code === 29) {
      error = new Error('未登录');
      //未登录
      window.location.href = `${window.location.origin}`;
    } else {
      error = new Error(typeof res.message === 'string' ? res.message : '');
    }
  } else {
    error = new Error(response.statusText);
  }

  if (error) {
    console.log(error);
    // @ts-ignore
    Message.error(error.message);
  }
  throw error;
}

/** 请求结果的error中添加trace信息 */
export function wrapResponseErrorWithTrace(
  result: Promise<any>,
  options: { headers?: Record<string, any> },
) {
  return result.catch((e) => {
    if (e && options?.headers?.['X-B3-Traceid']) {
      e.traceId = options.headers['X-B3-Traceid'];
      e.timeStr = moment().format('YYYY-MM-DD HH:mm:ss');
    }
    throw e;
  });
}

/** 对fetch做了下简单的包装
 *
 * options添加了三个选项
 * - requestType?: 'json' | 'form'  // 默认值如果data是FormData的话就是'form', 否则默认值是 'json'
 * - responseType?: 'json' | 'blob'  // 默认值是json
 * - data?: {[key: string]: any} | FormData
 *
 * 示例：
 * ```js
 * // get 请求 /api/xxx?a=b&c=d
 * request('/api/xxx?a=b', { data: { c: 'd' }})
 *
 * // post 请求，发送json
 * request('/api/xxx', { method: 'POST', data: { c: 'd' }})
 *
 * // post 请求，发送form
 * request('/api/xxx', { method: 'POST', requestType: 'form', data: { c: 'd' }})
 *
 *
 * // post 请求，上传文件
 * let fileList = document.querySelector('input[type="file"]').files
 * const formData = new FormData()
 * fileList.forEach(file => {
 *    formData.append('files[]', file)
 * })
 * request('/api/xxx', { method: 'POST', data: formData})
 *
 * ```
 *
 * fetch 的额外选项参考 https://github.com/github/fetch
 *
 * @param url
 * @param options
 */
export default async function request(url: string, options: Options = {}) {
  const { url: resultUrl, options: resultOptions } = parseOptions({
    url,
    options,
  });
  try {
    // @ts-ignore
    const response = await dvaFetch(resultUrl, resultOptions);
    return wrapResponseErrorWithTrace(
      parseResponse(response, resultOptions),
      resultOptions,
    );
  } catch (e) {
    return wrapResponseErrorWithTrace(Promise.reject(e), resultOptions);
  }
}

/**
 * 提供get和post两个快捷方法用于请求json数据。
 * 多数情况下我们只用得到get和post就足够了。
 * 后端新接口接收数据类型理论上都应该优先使用json格式而不是form，如果不是，需要后端修改。
 */
export function get<T>(
  url: string,
  params?: Record<string, any>,
): ApiReturn<T> {
  if (params) {
    url += (url.indexOf('?') > 0 ? '&' : '?') + qs.stringify(params);
  }
  return request(url, {
    method: 'GET',
  });
}

export function post<T>(url: string, data?: Record<string, any>): ApiReturn<T> {
  return request(url, {
    method: 'POST',
    data,
  });
}
